package com.dhcc.core.framework.security;

import java.util.List;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;

import com.dhcc.core.framework.constant.Constant;
import com.dhcc.core.framework.util.ArrayUtil;
import com.dhcc.core.framework.util.CommonUtil;
import com.dhcc.core.framework.util.HttpContextUtil;
import com.dhcc.core.framework.util.IpUtil;

import com.dhcc.core.modules.system.cache.dept.DeptCache;
import com.dhcc.core.modules.system.cache.user.UserCache;
import com.dhcc.core.modules.system.entity.OnlineSession.OnlineStatus;
import com.dhcc.core.modules.system.entity.User;
import com.dhcc.core.modules.system.entity.UserOnline;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;

import eu.bitwalker.useragentutils.UserAgent;

/**
 * shiro工具类
 *
 * @ClassName: ShiroKit
 * @Description: 描述
 * @author: cyf
 * @date: 2018年1月2日 上午10:00:42
 */
public class ShiroKit {

    private static final String NAMES_DELIMETER = ",";

    /**
     * 加盐参数
     */
    public final static String hashAlgorithmName = "MD5";

    /**
     * 循环次数
     */
    public final static int hashIterations = 1024;

    /**
     * shiro密码加密工具类
     *
     * @param credentials
     *            密码
     * @param saltSource
     *            密码盐
     * @return
     */
    public static String md5(String credentials, String saltSource) {
        ByteSource salt = new Md5Hash(saltSource);
        return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations).toString();
    }

    /**
     * 获取随机盐值
     *
     * @param length
     * @return
     */
    public static String getRandomSalt(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 获取当前 Subject
     *
     * @return Subject
     */
    public static Subject getSubject() {
        return SecurityUtils.getSubject();
    }

    /**
     * 获取封装的 User
     *
     * @return User
     */
    public static User getUser() {
        if (isGuest()) {
            return null;
        } else {
            return (User) getSubject().getPrincipals().getPrimaryPrincipal();
        }
    }

    /**
     * 当前用户是否是admin
     *
     * @Title: isAdmin
     * @Description: 描述
     * @return
     * @return: boolean
     */
    public static boolean isAdmin() {
        return CommonUtil.equals(getUser().getId(), Constant.ADMIN_ID);
    }

    /**
     * 从shiro获取session
     *
     */
    public static Session getSession() {
        return getSubject().getSession();
    }

    /**
     * 用户Online
     *
     * @Title: getCurrentUserOnline
     * @Description: 描述
     * @return
     * @return: UserOnline
     */
    public static UserOnline getCurrentUserOnline() {
        UserOnline userOnline = new UserOnline();
        User user = (User) getUser();
        if (user != null) {
            Session session = (Session) getSession();
            userOnline.setName(user.getName());
            userOnline.setLoginName(user.getLoginName());
            userOnline.setDeptName(user.getDeptName());
            userOnline.setPositionName(user.getPositionName());
            userOnline.setId((String) session.getId());
            userOnline.setStartTimestamp(session.getStartTimestamp());
            userOnline.setLastAccessTime(session.getLastAccessTime());
            Long timeout = session.getTimeout();
            if (timeout == 0L) {
                userOnline.setStatus(OnlineStatus.off_line);
            }
            userOnline.setTimeout(timeout);
        }
        HttpServletRequest request = HttpContextUtil.getRequest();
        if (request != null) {
            UserAgent userAgent = UserAgent.parseUserAgentString(HttpContextUtil.getRequest().getHeader("User-Agent"));
            // 获取客户端操作系统
            String os = userAgent.getOperatingSystem().getName();
            // 获取客户端浏览器
            String browser = userAgent.getBrowser().getName();
            userOnline.setIpaddr(IpUtil.getIpAddr(request));
            userOnline.setBrowser(browser);
            userOnline.setOs(os);
        }
        return userOnline;
    }

    /**
     * 获取shiro指定的sessionKey
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getSessionAttr(String key) {
        Session session = getSession();
        return session != null ? (T) session.getAttribute(key) : null;
    }

    /**
     * 设置shiro指定的sessionKey
     *
     */
    public static void setSessionAttr(String key, Object value) {
        Session session = getSession();
        session.setAttribute(key, value);
    }

    /**
     * 移除shiro指定的sessionKey
     */
    public static void removeSessionAttr(String key) {
        Session session = getSession();
        if (session != null) {
            session.removeAttribute(key);
        }
    }

    /**
     * 验证当前用户是否属于该角色？,使用时与lacksRole 搭配使用
     *
     * @param roleName
     *            角色名
     * @return 属于该角色：true，否则false
     */
    public static boolean hasRole(String roleName) {
        return PermissionKit.hasRole(getSubject(),roleName);
    }

    /**
     * 与hasRole标签逻辑相反，当用户不属于该角色时验证通过。
     *
     * @param roleName
     *            角色名
     * @return 不属于该角色：true，否则false
     */
    public static boolean lacksRole(String roleName) {
        return !hasRole(roleName);
    }

    /**
     * 验证当前用户是否属于以下任意一个角色。
     *
     * @param roleNames
     *            角色列表
     * @return 属于:true,否则false
     */
    public static boolean hasAnyRoles(String roleNames) {
        boolean hasAnyRole = false;
        if (getSubject() != null && roleNames != null && roleNames.length() > 0) {
            for (String role : roleNames.split(NAMES_DELIMETER)) {
                if (hasRole(role.trim())) {
                    hasAnyRole = true;
                    break;
                }
            }
        }
        return hasAnyRole;
    }

    /**
     * 验证当前用户是否属于以下所有角色。
     *
     * @param roleNames
     *            角色列表
     * @return 属于:true,否则false
     */
    public static boolean hasAllRoles(String roleNames) {
        boolean hasAllRole = true;
        if (getSubject() != null && roleNames != null && roleNames.length() > 0) {
            for (String role : roleNames.split(NAMES_DELIMETER)) {
                if (!hasRole(role.trim())) {
                    hasAllRole = false;
                    break;
                }
            }
        }
        return hasAllRole;
    }

    /**
     * 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用
     *
     * @param permission
     *            权限名
     * @return 拥有权限：true，否则false
     */
    public static boolean hasPermission(String permission) {
        return PermissionKit.hasPermission(getSubject(),permission);
    }

    /**
     * 与hasPermission标签逻辑相反，当前用户没有制定权限时，验证通过。
     *
     * @param permission
     *            权限名
     * @return 拥有权限：true，否则false
     */
    public static boolean lacksPermission(String permission) {
        return !hasPermission(permission);
    }

    /**
     * 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用
     *
     * @param permission
     *            权限名
     * @return 拥有权限：true，否则false
     */
    public static boolean hasAnyPermission(String permissions) {
        boolean hasAllPermission = false;
        if (getSubject() != null && permissions != null && permissions.length() > 0) {
            for (String role : permissions.split(NAMES_DELIMETER)) {
                if (hasPermission(role.trim())) {
                    hasAllPermission = true;
                    break;
                }
            }
        }
        return hasAllPermission;
    }

    /**
     * 已认证通过的用户。不包含已记住的用户，这是与user标签的区别所在。与notAuthenticated搭配使用
     *
     * @return 通过身份验证：true，否则false
     */
    public static boolean isAuthenticated() {
        return getSubject() != null && getSubject().isAuthenticated();
    }

    /**
     * 未认证通过用户，与authenticated标签相对应。与guest标签的区别是，该标签包含已记住用户。。
     *
     * @return 没有通过身份验证：true，否则false
     */
    public static boolean notAuthenticated() {
        return !isAuthenticated();
    }

    /**
     * 认证通过或已记住的用户。与guset搭配使用。
     *
     * @return 用户：true，否则 false
     */
    public static boolean isUser() {
        return getSubject() != null && getSubject().getPrincipal() != null;
    }

    /**
     * 验证当前用户是否为“访客”，即未认证（包含未记住）的用户。用user搭配使用
     *
     * @return 访客：true，否则false
     */
    public static boolean isGuest() {
        return !isUser();
    }

    /**
     * 部门数据权限
     */
    public static List<Long> getDepDataScope() {
        long depId = getUser().getDeptId();
        // 获取当前部门的子部门
        List<Long> subDepIds = DeptCache.me().getDeptIdsByPId(depId);
        subDepIds.add(depId);
        return subDepIds;

    }

    /**
     * 检查角色
     *
     * @Title: checkRoles
     * @Description: 描述
     * @param roles
     * @return
     * @return: boolean
     */
    public static boolean checkRoles(Object[] roles) {
        User user = getUser();
        if (null == user) {
            return false;
        }
        String join = ArrayUtil.arrayToString(roles, ",");
        if (hasAnyRoles(join)) {
            return true;
        }
        return false;
    }

    /**
     * 检查权限
     *
     * @Title: checkPermissions
     * @Description: 描述
     * @param permissions
     * @return
     * @return: boolean
     */
    public static boolean checkPermissions(Object[] permissions) {
        User user = getUser();
        if (null == user) {
            return false;
        }
        String join = ArrayUtil.arrayToString(permissions, ",");
        if (hasAnyPermission(join)) {
            return true;
        }
        return false;
    }

    /**
     * 刷新用户信息
     * @param userInfo
     */
    public static void refreshUser() {
        User user = getUser();
        if (null == user) {
            return;
        }
    	Subject subject = getSubject();
    	PrincipalCollection principalCollection = subject.getPrincipals();
        String realmName = principalCollection.getRealmNames().iterator().next();
        user = UserCache.me().getUserById(user.getId());
        user = user.fill();
    	PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(user, realmName);
    	subject.runAs(newPrincipalCollection);
    }
}
